package index;

import java.io.IOException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.text.StyledEditorKit.BoldAction;

import net.barashev.seclub.io.BufferManager;
import net.barashev.seclub.io.DataMarshaller;
import net.barashev.seclub.io.FixedLengthRecordBlock;
import net.barashev.seclub.io.IteratingSlot;
import net.barashev.seclub.io.OutputSlot;
import net.barashev.seclub.io.file.FileManager;
import net.barashev.seclub.io.file.VFile;
import net.barashev.seclub.io.marshaller.IntegerComparator;

public class IndexSorter {
	/**
	 * 
	 * @param tempFiles
	 * @param outputFile
	 * @param bufferMgr
	 * @throws IOException
	 */
	public static void multiwayMergeSort(List<VFile<Integer>> tempFiles,
			VFile<Integer> outputFile, BufferManager bufferMgr)
			throws IOException {
		int optimSlotSize = bufferMgr.getBufferCount()/(tempFiles.size() + 1);
		assert optimSlotSize >= 1;
		multiwayMergeSort(tempFiles, outputFile, bufferMgr,optimSlotSize);
	}
	
	public static void multiwayMergeSort(List<VFile<Integer>> tempFiles,
			VFile<Integer> outputFile, BufferManager bufferMgr, int slotSize)
			throws IOException {
		HashMap<IteratingSlot<Integer, DataMarshaller<Integer>>, Integer> mergeSlots = initialPullHeads(
				bufferMgr, tempFiles, slotSize);

		OutputSlot<Integer, DataMarshaller<Integer>> outputSlot = bufferMgr
				.allocateOutputSlot(outputFile);

		Comparator<Integer> comparator = new IntegerComparator();
		mergeSortedSublists(comparator, mergeSlots, outputSlot);
		outputSlot.flush();
		outputSlot.release();
	}

	private static <T> HashMap<IteratingSlot<T, DataMarshaller<T>>, Integer> initialPullHeads(
			BufferManager bufferMgr, List<VFile<T>> tempFiles, int slotSize)
			throws IOException {
		HashMap<IteratingSlot<T, DataMarshaller<T>>, Integer> mergeSlots = new HashMap<IteratingSlot<T, DataMarshaller<T>>, Integer>();
		for (int i = 0; i < tempFiles.size(); i++) {
			IteratingSlot<T, DataMarshaller<T>> mergeSlot = bufferMgr
					.allocateIteratingSlot(slotSize, tempFiles.get(i));
			boolean pulled = mergeSlot.pull();
			assert pulled;
			mergeSlots.put(mergeSlot, 0);
		}
		return mergeSlots;
	}

	private static void mergeSortedSublists(
			Comparator<Integer> comparator,
			HashMap<IteratingSlot<Integer, DataMarshaller<Integer>>, Integer> mergeSlots,
			OutputSlot<Integer, DataMarshaller<Integer>> outputSlot)
			throws IOException {

		while (!mergeSlots.isEmpty()) {
			Integer minRecord = null;
			Map.Entry<IteratingSlot<Integer, DataMarshaller<Integer>>, Integer> minEntry = null;
			for (Iterator<Map.Entry<IteratingSlot<Integer, DataMarshaller<Integer>>, Integer>> it = mergeSlots
					.entrySet().iterator(); it.hasNext();) {
				Map.Entry<IteratingSlot<Integer, DataMarshaller<Integer>>, Integer> entry = it
						.next();
				IteratingSlot<Integer, DataMarshaller<Integer>> slot = entry
						.getKey();
				Integer idxRecord = entry.getValue();
				Integer topRecord = slot.getBlock(0).get(idxRecord);
				if ((minRecord == null)
						|| (comparator.compare(minRecord, topRecord) > 0)) {
					minRecord = topRecord;
					minEntry = entry;
				}
			}
			boolean needDelete = appendPostingToOutputSlot(minRecord,
					minEntry, outputSlot);
			if (needDelete) {
				// it.remove();
				mergeSlots.remove(minEntry.getKey());
			}

		}
	}

	private static boolean appendPostingToOutputSlot(
			final Integer record,
			final Entry<IteratingSlot<Integer, DataMarshaller<Integer>>, Integer> entry,
			final OutputSlot<Integer, DataMarshaller<Integer>> outputSlot)
			throws IOException {

		assert (record != null);
		IteratingSlot<Integer, DataMarshaller<Integer>> slot = entry.getKey();
		//System.out.print(" id = " + record);
		outputSlot.append(record);
		incIdx_And_checkOnFull(entry);

		Integer n_AmounPosition = slot.getBlock(0).get(entry.getValue());
		// n = amounPosition + 1(for MetaData)
		//System.out.print(" N = " + n_AmounPosition + " position : ");
		outputSlot.append(n_AmounPosition);
		incIdx_And_checkOnFull(entry);
		boolean needDelete = false;
		for (int i = 0; i < n_AmounPosition; i++) {
			Integer position = slot.getBlock(0).get(entry.getValue());
			//System.out.print(position + " , ");
			outputSlot.append(position);
			needDelete = incIdx_And_checkOnFull(entry);
		}
		//System.out.println();
		return needDelete;
	}

	private static boolean incIdx_And_checkOnFull(
			final Entry<IteratingSlot<Integer, DataMarshaller<Integer>>, Integer> entry)
			throws IOException {

		entry.setValue(entry.getValue() + 1);

		if (entry.getValue() == entry.getKey().getRecordCount()) {
			if (entry.getKey().pull()) {
				entry.setValue(0);
			} else {
				// it.remove();
				return true;
			}
		}
		return false;
	}
}
